use std::convert::Infallible;

use axum::{
    Json, Router,
    extract::{Path, Query, State},
    response::{
        Sse,
        sse::{Event, KeepAlive},
    },
    routing::{delete, get, patch, post},
};
use chrono::Local;
use ru::resp::{JsonRes, RError, Resp};
use tokio_stream::{Stream, StreamExt, wrappers::BroadcastStream};
use tracing::{error, info};

use crate::{
    models::{
        AddReq, AriaParams, Category, ClearReq, Detail, QueryReq, SelectFileReq, Status, Task,
    },
    repo::TaskRepo,
    state::AppState,
};

pub fn task_router() -> Router<AppState> {
    Router::new()
        .route("/aria2/version", get(handle_get_version))
        .route("/task/add", post(handle_add_task))
        .route("/tasks", get(handle_query_tasks))
        .route("/select/file", post(handle_select_file))
        .route("/task/{id}/pause", patch(handle_pause_task))
        .route("/task/{id}/start", patch(handle_start_task))
        .route("/task/{id}", delete(handle_delete_task))
        .route("/task/notify", get(handle_task_notify))
        .route("/task/clear", post(handle_clear_task))
}

async fn handle_get_version(State(state): State<AppState>) -> JsonRes<String> {
    let version = state.aria_rpc.get_version().await?;

    Ok(Resp::msg(version))
}

async fn handle_add_task(
    State(state): State<AppState>,
    Json(req): Json<AddReq>,
) -> JsonRes<String> {
    let AppState { db, .. } = &state;

    if req.uri.is_empty() {
        return Err(RError::new("uri为空"));
    }

    let is = TaskRepo::is_exist_uri(db, req.uri.clone()).await?;
    if is {
        return Err(RError::new("下载任务已存在"));
    }

    let name = match req.cate {
        Category::File => req
            .name
            .or(req.uri.rsplit_once('/').map(|(_, n)| n.to_owned()))
            .unwrap_or("unknown".into()),
        Category::Magnet => req.name.unwrap_or(format!(
            "{} magnet task",
            Local::now().format("%Y%m%d%H%M%S")
        )),
        Category::BitTorrent => req
            .name
            .unwrap_or(format!("{} bt task", Local::now().format("%Y%m%d%H%M%S"))),
    };

    let task = Task {
        uri: req.uri,
        status: Status::Waiting,
        category: req.cate,
        name,
        created_at: Local::now().timestamp_millis(),
        detail_data: Detail {
            dir: req.dir.unwrap_or_default(),
            ..Default::default()
        },
        ..Default::default()
    };

    TaskRepo::create(db, task).await?;

    Ok(Resp::msg("任务已创建"))
}

async fn handle_query_tasks(
    State(state): State<AppState>,
    Query(req): Query<QueryReq>,
) -> JsonRes<Vec<Task>> {
    let list = TaskRepo::get_list(&state.db, req).await?;

    Ok(Resp::data(list))
}

async fn handle_select_file(
    State(state): State<AppState>,
    Json(req): Json<SelectFileReq>,
) -> JsonRes<String> {
    if req.gid.is_empty() {
        return Err(RError::new("gid为空"));
    }

    let p = AriaParams {
        gid: req.gid,
        select_file: Some(req.select_file),
        ..Default::default()
    };

    let res = state.aria_rpc.select_file(p).await?;
    info!("select file res: {}", res);

    Ok(Resp::msg("文件已更新"))
}

async fn handle_pause_task(State(state): State<AppState>, Path(id): Path<i32>) -> JsonRes<String> {
    let AppState { db, aria_rpc, .. } = &state;

    if id == 0 {
        return Err(RError::new("id为空"));
    }

    let mut task = TaskRepo::get(db, id)
        .await?
        .ok_or(RError::new("任务不存在"))?;

    let res = aria_rpc.pause(task.gid.clone()).await?;
    info!("pause res: {}", res);

    task.status = Status::Pause;
    TaskRepo::update_status(db, task).await?;

    Ok(Resp::msg("任务已暂停"))
}

async fn handle_start_task(State(state): State<AppState>, Path(id): Path<i32>) -> JsonRes<String> {
    let AppState { db, aria_rpc, .. } = &state;

    if id == 0 {
        return Err(RError::new("id为空"));
    }

    let mut task = TaskRepo::get(db, id)
        .await?
        .ok_or(RError::new("任务不存在"))?;

    let res = aria_rpc.unpause(task.gid.clone()).await?;
    info!("unpause res: {}", res);

    task.status = Status::Active;
    TaskRepo::update_status(db, task).await?;

    Ok(Resp::msg("任务已开始"))
}

async fn handle_delete_task(State(state): State<AppState>, Path(id): Path<i32>) -> JsonRes<String> {
    let AppState { db, aria_rpc, .. } = &state;

    if id == 0 {
        return Err(RError::new("id为空"));
    }

    let task = TaskRepo::get(db, id)
        .await?
        .ok_or(RError::new("任务不存在"))?;

    TaskRepo::delete(db, id).await?;

    if task.status == Status::Active {
        let res = aria_rpc.force_remove(task.gid.clone()).await?;
        info!("force remove {}, result: {}", task.gid, res);
    }

    Ok(Resp::msg("任务已删除"))
}

async fn handle_task_notify(
    State(state): State<AppState>,
) -> Sse<impl Stream<Item = Result<Event, Infallible>>> {
    let rx = state.notifier.subscribe();

    let stream = BroadcastStream::new(rx).filter_map(|result| match result {
        Ok(task) => {
            serde_json::to_string(&task).map_or(None, |data| Some(Ok(Event::default().data(data))))
        }
        Err(e) => {
            error!("BroadcastStreamRecvError: {}", e);
            None
        }
    });

    Sse::new(stream).keep_alive(KeepAlive::default())
}

async fn handle_clear_task(
    State(state): State<AppState>,
    Json(req): Json<ClearReq>,
) -> JsonRes<String> {
    let AppState { db, aria_rpc, .. } = &state;

    if req.gids.is_empty() {
        return Err(RError::new("清除列表为空"));
    }

    let list = TaskRepo::get_list_by_gids(db, req.gids).await?;

    for task in list {
        TaskRepo::delete(db, task.id.unwrap()).await?;

        if task.status == Status::Active {
            let _ = aria_rpc.force_remove(task.gid).await?;
        }
    }

    Ok(Resp::msg("任务已清除"))
}
